<?php

$memsize = 128*256;
$nanovm = new NanoVM;
$maxcycle = 1000000;
$cycles = 0;
$NanoLog = '';

function NanoLog($log) { global $NanoLog; $NanoLog .= $log . "\n"; }

class NanoVM
{
	var $prgm = array();
	var $currentprgm = null;
	var $mem = array();
	var $checkstop = null;
	
	function AddProgram($prgm)
	{
		$this->prgm[$prgm->id] = $prgm;
		if($this->currentprgm === null) $this->currentprgm = reset($this->prgm);
		/*$prgm->id = count($this->prgm) - 1;*/
	}
	
	function RemoveProgram($prgm)
	{
		unset($this->prgm[$prgm->id]);
		if(count($this->prgm) == 0)
		{
			NanoStopExecution();
			NanoLog("Execution stopped : aucun programme à exécuter");
		}
	}
	
	function GetNextProgram()
	{
		if(count($this->prgm) == 0) return false;
		/*if($this->currentprgm >= count($this->prgm))
			return reset($this->prgm);*/
		$prgm = current($this->prgm);
		if($prgm == false) $prgm = reset($this->prgm);
		next($this->prgm);
		return $prgm;
	}
}

class NanoPrgm
{
	var $name = '';
	var $id = -1;
	var $header = '';
	var $code = array();
	var $options;
	var $ctx = array();
	var $currentctx = null;
	
	function Map($addr)
	{
		global $nanovm;
		$len = count($this->code);
		for($i = 0; $i<$len; $i++)
		{
			$nanovm->mem[$addr + $i]['val'] = $this->code[$i];
			$nanovm->mem[$addr + $i]['type'] = 'prgm';
			$nanovm->mem[$addr + $i]['prgm'] = $this->name;
		}
	}
	
	function CreateContext()
	{
		$ctx = NanoCreateContext($this, count($this->ctx), $this->options);
		$this->AddContext($ctx);
		return $ctx;
	}
	
	function AddContext($ctx)
	{
		$this->ctx[] = $ctx;
		if($this->currentctx === null)
			$this->currentctx = 0;
		$ctx->id = count($this->ctx) - 1;
	}
	
	function RemoveContext($ctx)
	{
		global $nanovm;
		unset($this->ctx[$ctx->id]);
		if(count($this->ctx) == 0)
			$nanovm->RemoveProgram($this);
		NanoLog($this->name . ' stopped');
	}
	
	function GetNextContext()
	{
		if($this->currentctx >= count($this->ctx))
			$this->currentctx = 0;
		return (count($this->ctx)?$this->ctx[$this->currentctx++]:false);
	}
}

$registers = array('ax', 'bx', 'cx', 'dx', 'ip', 'sp', 'fx');
$flags = array('fz', 'fnz', 'fc', 'fnc', 'fp', 'fn');
define('FZ', 1);
define('FNZ', 2);
define('FC', 4);
define('FNC', 8);
define('FP', 16);
define('FN', 32);
class NanoContext
{
	var $reg = array('ax' => 0, 'bx' => 0, 'cx' => 0, 'dx' => 0, 'fx' => 0, 'ip' => 0, 'sp' => 0, 'bp' => 0);
	var $options;
	var $host;
	var $name;
	
	function SetReg($reg, $val)
	{
		global $memsize;
		if($reg == 'ip') $val = $val%$memsize;
		$this->reg[$reg] = $val;
	}
	
	function IncIP()
	{
		$this->reg['ip']++;
	}
	
	function SetFlags($f)
	{
		if($f & FZ) $this->reg['fx'] |= 1;
		if($f & FNZ) $this->reg['fx'] &= ~1;
		if($f & FC) $this->reg['fx'] |= 2;
		if($f & FNC) $this->reg['fx'] &= ~2;
		if($f & FP) $this->reg['fx'] |= 4;
		if($f & FN) $this->reg['fx'] &= ~4;
	}
	
	function GetFlag($idx)
	{
		global $flags;
		if(!isset($flags[$idx - 1]))
		{
			NanoCrashContext($this, 'NANO_INVALID_FLAG');
			return null;
		}
		$flag = $flags[$idx - 1];
		$bit = array('fz' => array(1,1), 'fnz' => array(1,0), 'fc' => array(2,1), 'fnc' => array(2,0), 'fp' => array(4,1), 'fn' => array(4,0));
		return ($this->reg['fx'] & $bit[$flag][0])==$bit[$flag][1];
	}
}

$instructions = array(
	/* mov reg32, reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$ctx->SetReg($regd, $ctx->reg[$regs]);
		return true;
	},
	/* mov reg32, imm32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowl);
		if($regd === false) return false;
		$ctx->SetReg($regd, $addr);
		return true;
	},
	/* mov reg32, (reg32) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$mem = NanoGetMem($ctx, $ctx->reg[$regs]);
		if($mem === false) return false;
		$ctx->SetReg($regd, $mem);
		return true;
	},
	/* mov reg32, (addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowl);
		if($regd === false) return false;
		$mem = NanoGetMem($ctx, addr);
		if($mem === false) return false;
		$ctx->SetReg($regd, $mem);
		return true;
	},
	/* mov reg32, (+addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowl);
		if($regd === false) return false;
		$mem = NanoGetRelMem($ctx, $addr);
		if($mem === false) return false;
		$ctx->SetReg($regd, $mem);
		return true;
	},
	/* mov reg32, label */
	function($ctx, $lowh, $lowl, $addr)
	{
		NanoCrashContext($ctx, 'NANO_BAD_OP_CODE');
		return false;
	},
	/* mov (reg32), reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		return NanoSetMem($ctx, $ctx->reg[$regd], $ctx->reg[$regs]);
	},
	/* mov (addr), reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regs = NanoGetRegister($ctx, $lowl);
		if($regs === false) return false;
		return NanoSetMem($ctx, $addr, $ctx->reg[$regs]);
	},
	/* mov (+addr), reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regs = NanoGetRegister($ctx, $lowl);
		if($regs === false) return false;
		return NanoSetRelMem($ctx, $addr, $ctx->reg[$regs]);
	},
	/* add reg32, reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$ctx->SetReg($regd, $ctx->reg[$regd] + $ctx->reg[$regs]);
		return true;
	},
	/* add reg32, (reg32) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$mem = NanoGetMem($ctx, $ctx->reg[$regs]);
		if($mem === false) return false;
		$ctx->SetReg($regd, $ctx->reg[$regd] + $mem);
		return true;
	},
	/* add reg32, imm32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] + $addr);
		return true;
	},
	/* add reg32, (addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetMem($ctx, $addr);
		if($mem === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] + $mem);
		return true;
	},
	/* add reg32, (+addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetRelMem($ctx, $addr);
		if($mem === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] + $mem);
		return true;
	},
	/* sub reg32, reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$ctx->SetReg($regd, $ctx->reg[$regd] - $ctx->reg[$regs]);
		return true;
	},
	/* sub reg32, (reg32) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$mem = NanoGetMem($ctx, $ctx->reg[$regs]);
		if($mem === false) return false;
		$ctx->SetReg($regd, $ctx->reg[$regd] - $mem);
		return true;
	},
	/* sub reg32, imm32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] - $addr);
		return true;
	},
	/* sub reg32, (addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		/**********************************************/
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetMem($ctx, addr);
		if($mem === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] - $mem);
		return true;
	},
	/* sub reg32, (+addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetRelMem($ctx, $addr);
		if($mem === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] - $mem);
		return true;
	},
	/* mul reg32, reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$ctx->SetReg($regd, $ctx->reg[$regd] * $ctx->reg[$regs]);
		return true;
	},
	/* mul reg32, (reg32) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$mem = NanoGetMem($ctx, $ctx->reg[$regs]);
		if($mem === false) return false;
		$ctx->SetReg($regd, $ctx->reg[$regd] * $mem);
		return true;
	},
	/* mul reg32, imm32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] * $addr);
		return true;
	},
	/* mul reg32, (addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetMem($ctx, addr);
		if($mem === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] - $mem);
		return true;
	},
	/* mul reg32, (+addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetRelMem($ctx, $addr);
		if($mem === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] * $mem);
		return true;
	},
	/* inc reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] + 1);
		return true;
	},
	/* inc (reg32) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetMem($ctx, $ctx->reg[$reg]);
		if($mem === false) return false;
		return NanoSetRelMem(ctx, $ctx->reg[$reg], $mem + 1);
	},
	/* dec reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] - 1);
		return true;
	},
	/* dec (reg32) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetMem($ctx, $ctx->reg[$reg]);
		if($mem === false) return false;
		return NanoSetRelMem(ctx, $ctx->reg[$reg], $mem - 1);
	},
	/* or reg32, reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$ctx->SetReg($regd, $ctx->reg[$regd] | $ctx->reg[$regs]);
		return true;
	},
	/* or reg32, (reg32) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$mem = NanoGetMem($ctx, $ctx->reg[$regs]);
		if($mem === false) return false;
		$ctx->SetReg($regd, $ctx->reg[$regd] | $mem);
		return true;
	},
	/* or reg32, imm32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] | $addr);
		return true;
	},
	/* or reg32, (addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetMem($ctx, addr);
		if($mem === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] | $mem);
		return true;
	},
	/* or reg32, (+addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetRelMem($ctx, $addr);
		if($mem === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] | $mem);
		return true;
	},
	/* and reg32, reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$ctx->SetReg($regd, $ctx->reg[$regd] & $ctx->reg[$regs]);
		return true;
	},
	/* and reg32, (reg32) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$mem = NanoGetMem($ctx, $ctx->reg[$regs]);
		if($mem === false) return false;
		$ctx->SetReg($regd, $ctx->reg[$regd] & $mem);
		return true;
	},
	/* and reg32, imm16 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] & $addr);
		return true;
	},
	/* and reg32, (addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetMem($ctx, addr);
		if($mem === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] & $mem);
		return true;
	},
	/* and reg32, (+addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetRelMem($ctx, $addr);
		if($mem === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] & $mem);
		return true;
	},
	/* xor reg32, reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$ctx->SetReg($regd, $ctx->reg[$regd] ^ $ctx->reg[$regs]);
		return true;
	},
	/* xor reg32, (reg32) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		$mem = NanoGetMem($ctx, $ctx->reg[$regs]);
		if($mem === false) return false;
		$ctx->SetReg($regd, $ctx->reg[$regd] ^ $mem);
		return true;
	},
	/* xor reg32, imm32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] ^ $addr);
		return true;
	},
	/* xor reg32, (addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetMem($ctx, addr);
		if($mem === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] ^ $mem);
		return true;
	},
	/* xor reg32, (+addr) */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		$mem = NanoGetRelMem($ctx, $addr);
		if($mem === false) return false;
		$ctx->SetReg($reg, $ctx->reg[$reg] ^ $mem);
		return true;
	},
	/* push reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		NanoSetMem($ctx, $ctx->reg['sp'], $ctx->reg[$reg]);
		$ctx->reg['sp']++;
	},
	/* pop reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$reg = NanoGetRegister($ctx, $lowl);
		if($reg === false) return false;
		if($ctx->reg['sp'] == $ctx->reg['bp'])
			NanoCrashContext($ctx, 'NANO_POP_EMPTY_STACK');
		else
		{
			$ctx->reg['sp']--;
			$val = NanoGetMem($ctx, $ctx->reg['sp']);
			$ctx->SetReg($reg, $val);
		}
	},
	null,null,null,null,null,null,null,null,null,null,null,null,null,null,
	/* ret */
	function($ctx, $lowh, $lowl, $addr)
	{
		if($ctx->reg['sp'] == $ctx->reg['bp'])
			NanoCrashContext($ctx, 'NANO_POP_EMPTY_STACK');
		else
		{
			$ctx->reg['sp']--;
			$val = NanoGetMem($ctx, $ctx->reg['sp']);
			$ctx->SetReg('ip', $val - 1);
		}
	},
	/* ret flag */
	function($ctx, $lowh, $lowl, $addr)
	{
		$flag = $ctx->GetFlag($lowl);
		if($flag === null) return false;
		if($flag)
		{
			if($ctx->reg['sp'] == $ctx->reg['bp'])
				NanoCrashContext($ctx, 'NANO_POP_EMPTY_STACK');
			else
			{
				$ctx->reg['sp']--;
				$val = NanoGetMem($ctx, $ctx->reg['sp']);
				$ctx->SetReg('ip', $val - 1);
			}
		}
	},
	/* jmp addr */
	function($ctx, $lowh, $lowl, $addr)
	{
		$ctx->SetReg('ip', $addr - 1);
		return true;
	},
	/* jmp +addr */
	function($ctx, $lowh, $lowl, $addr)
	{
		$ctx->SetReg('ip', $ctx->reg['ip'] + $addr - 1, false);
		return true;
	},
	/* jmp label */
	function($ctx, $lowh, $lowl, $addr)
	{
		NanoCrashContext($ctx, 'NANO_BAD_OP_CODE');
		return false;
	},
	/* jmp flag, addr */
	function($ctx, $lowh, $lowl, $addr)
	{
		$flag = $ctx->GetFlag($lowl);
		if($flag === null) return false;
		if($flag) $ctx->SetReg('ip', $addr - 1);
	},
	/* jmp flag, +addr */
	function($ctx, $lowh, $lowl, $addr)
	{
		$flag = $ctx->GetFlag($lowl);
		if($flag === null) return false;
		if($flag) $ctx->SetReg('ip', $ctx->reg['ip'] + $addr - 1);
	},
	/* jmp flag, label */
	function($ctx, $lowh, $lowl, $addr)
	{
		NanoCrashContext($ctx, 'NANO_BAD_OP_CODE');
		return false;
	},
	/* cmp reg32, reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowh);
		$regs = NanoGetRegister($ctx, $lowl);
		if($regd === false || $regs === false) return false;
		NanoCmp($ctx, $ctx->reg[$regd], $ctx->reg[$regs]);
		return true;
	},
	/* cmp reg32, imm32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowl);
		if($regd === false) return false;
		NanoCmp($ctx, $ctx->reg[$regd], $addr);
		return true;
	},
	/* test reg32 */
	function($ctx, $lowh, $lowl, $addr)
	{
		$regd = NanoGetRegister($ctx, $lowl);
		if($regd === false) return false;
		NanoCmp($ctx, $ctx->reg[$regd], 0);
		return true;
	});

/*echo '<pre>';
NanoExec(array('players' => array(array(0 => new UserInfos, 'addr' => 0, 'code' => 'nano:1::0x1b050000'))));
echo '</pre>';*/

function NanoExec($execinfos)
{
	global $memsize;
	global $nanovm;
	global $cycles;
	global $NanoLog;
	if($execinfos['do'] != 'serverexec') return false;
	NanoInitVM();
	foreach($execinfos['players'] as $player)
	{
		$code = $player['code'];
		//$addr = ($player['addr'] == -1?rand(0, $memsize-1):$player['addr']);
		$prgm = NanoCreateProgram($player[0], $code, null);
		$addr = $prgm->header['addr']; /**/
		$prgm->Map($addr);
		$prgm->ctx[0]->SetReg('ip', $addr);
		$nanovm->AddProgram($prgm);
		NanoLog($player[0]->name . '(' . $prgm->id . ') : ' . $addr);
	}
	if(isset($execinfos['checkstop'])) $nanovm->checkstop = $execinfos['checkstop'];
	$res = NanoStartExecution();
	NanoLog('End : ' . $cycles . ' cycles');
	return array('stop' => ($res?'cyclelimit':'checked'), 'vm' => $nanovm, 'log' => $NanoLog);
}

function NanoInitVM()
{
	NanoCreateMemory();
}

function NanoCreateMemory()
{
	global $memsize;
	global $nanovm;
	for($i = 0; $i<$memsize; $i++)
	{
		$nanovm->mem[$i] = array();
		$nanovm->mem[$i]['val'] = 0;
		$nanovm->mem[$i]['type'] = 'empty';
	}
}

function NanoReset()
{
	global $nanovm;
	unset($nanovm);
	$nanovm = new NanoVM;
	NanoInitVM();
}

function NanoCreateProgram($player, $code, $options)
{
	$prgm = new NanoPrgm;
	$prgm->name = $player->name;
	$prgm->id = $player->id;
	$data = explode("::", $code);
	$header = explode(':', $data[0]);
	foreach($header as $h) { $d = explode('=', $h); $prgm->header[$d[0]] = $d[1]; }
	$prgm->code = explode(":", $data[1]);
	$prgm->options = $options;
	$prgm->currentctx = null;
	$prgm->CreateContext();
	return $prgm;
}

function NanoCreateContext($host, $name, $options)
{
	$context = new NanoContext;
	$context->options = $options;
	$context->host = $host;
	$context->name = $name;
	return $context;
}

function NanoStartExecution()
{
	global $maxcycle;
	global $cycles;
	for($cycles = 0; $cycles<$maxcycle; $cycles++)
		if(NanoClock() == false) return false;
	return true;
}

function NanoStopExecution()
{
	global $maxcycle;
	$maxcycle = -1;
}

function NanoClock()
{
	global $nanovm;
	$prgm = $nanovm->GetNextProgram();
	if($prgm == false)
	{
		NanoStopExecution();
		NanoLog("Execution stopped : aucun programme à exécuter");
		return false;
	}
	$ctx = $prgm->GetNextContext();
	if($ctx == false)
	{
		NanoStopExecution();
		NanoLog("Internal error : zombie : " . $prgm->name);
		return false;
	}
	NanoExecuteContext($ctx);
	return NanoCheckStop();
}

function NanoCheckStop()
{
	global $nanovm;
	if($nanovm->checkstop !== null)
		return call_user_func($nanovm->checkstop);
	return true;
}

function NanoFightCheckStop()
{
	global $nanovm;
	$prgmleft = count($nanovm->prgm);
	if($prgmleft == 1) return false;
	return true;
}

function NanoExecuteContext($ctx)
{
	$inst = NanoGetNextInstruction($ctx);
	if($inst == false) return false;
	NanoExecuteInstruction($ctx, $inst);
}

function NanoGetNextInstruction($ctx)
{
	global $memsize;
	global $nanovm;
	$addr = $ctx->reg['ip']%$memsize;
	if($nanovm->mem[$addr]['type'] == 'protected' && isset($nanovm->mem[$addr]['protect']['exec']))
	{
		NanoCrashContext($ctx, 'NANO_EXEC_PROTECTED_MEMORY');
		return false;
	}
	return $nanovm->mem[$addr];
}

function NanoGetRegister($ctx, $idx)
{
	global $registers;
	if(!isset($registers[$idx - 1]))
	{
		NanoCrashContext($ctx, 'NANO_INVALID_REGISTER');
		return false;
	}
	return $registers[$idx - 1];
}

function NanoGetMem($ctx, $addr)
{
	global $memsize;
	global $nanovm;
	$addr = $addr%$memsize;
	$mem = $nanovm->mem[$addr];
	if($mem['type'] == 'protected')
	{
		if(isset($mem['protect']['read']))
		{
			NanoCrashContext($ctx, 'NANO_READ_PROTECTED_MEMORY');
			return false;
		}
	}
	return $mem['val'];
}

function NanoGetRelMem($ctx, $reladdr)
{
	global $memsize;
	$addr = ($ctx->reg['ip'] + $reladdr)%$memsize;
	return NanoGetMem($ctx, $addr);
}

function NanoSetMem($ctx, $addr, $val)
{
	global $memsize;
	global $nanovm;
	$addr = $addr%$memsize;
	$mem = &$nanovm->mem[$addr];
	if($mem['type'] == 'protected')
	{
		if(isset($mem['protect']['write']))
		{
			NanoCrashContext($ctx, 'NANO_WRITE_PROTECTED_MEMORY');
			return false;
		}
	}
	
	$mem['val'] = $val;
	$mem['type'] = 'prgm';
	$mem['prgm'] = $ctx->host->name;
	return true;
}

function NanoSetRelMem($ctx, $reladdr, $val)
{
	global $memsize;
	$addr = ($ctx->reg['ip'] + $reladdr)%$memsize;
	return NanoSetMem($ctx, $addr, $val);
}

function NanoCmp($ctx, $a, $b)
{
	if($a == $b) $ctx->SetFlags(FZ|FP);
	else if($a - $b > 0) $ctx->SetFlags(FNZ|FP);
	else $ctx->SetFlags(FNZ|FN);
}

function NanoExecuteInstruction($ctx, $inst)
{
	global $instructions;
	$val = intval($inst['val'], 0);
	$op = (($val&0xFF000000) >> 24) - 1;
	$lowh = ($val&0xF00000) >> 20;
	$lowl = ($val&0xF0000) >> 16;
	$addr = $val&0xFFFF;
	if($addr&0x8000) $addr |= 0xFFFF0000;
	if(!isset($instructions[$op]) || $instructions[$op] === null) { NanoCrashContext($ctx, 'NANO_BAD_OP_CODE'); return false; }
	$instructions[$op]($ctx, $lowh, $lowl, $addr);
	$ctx->IncIP();
}

function NanoCrashContext($ctx, $error)
{
	global $cycles;
	$ctx->host->RemoveContext($ctx);
	NanoLog('Crash (' . $cycles . ') : ' . $ctx->host->name . '.thread' . $ctx->name . "\n" . $error);
}

?>